using System;
using System.Collections.Generic;
using System.Text;
using Jbe.TestSuite.TestDevices.FunctionGenerator.Interface;
using Jbe.TestSuite.TestDevices.Interface;
using System.Threading;
using System.Diagnostics;
using Jbe.TestSuite.Infrastructure.Interface;
using System.ComponentModel;
using Microsoft.Practices.Composite.Events;
using Jbe.Composite.Extensions;
using System.Reflection;

namespace Jbe.TestSuite.TestDevices.FunctionGenerator.Driver.Domain
{
    public class FunctionGeneratorDriver : Model, IFunctionGenerator, IDisposable
    {
        private static readonly PropertyInfo ConfigProperty = TypeManager.GetProperty<FunctionGeneratorDriver>(x => x.Config);


        /// <summary>
        /// Define a delegate for calculation of the wave form amplitude at the specified time.
        /// </summary>
        public delegate double WaveFormStrategy(double amplitude, double frequency, double time);


        private IEventAggregator eventAggregator;
        private IFunctionGeneratorConfig config;
        private Timer timer;
        private bool isActive;
        private DateTime startTime;


        public FunctionGeneratorDriver(IEventAggregator eventAggregator)
        {
            this.eventAggregator = eventAggregator;

            config = new FunctionGeneratorConfig()
            {
                WaveForm = WaveForm.Sine,
                Amplitude = 50,
                Frequency = 2
            };

            timer = new Timer(new TimerCallback(FireSignalSample));
            isActive = false;
        }


        public IFunctionGeneratorConfig Config
        {
            get { return config; }
            set 
            {
                if (config != value)
                {
                    value.Lock();
                    Interlocked.Exchange<IFunctionGeneratorConfig>(ref config, value);
                    RaisePropertyChanged(ConfigProperty);
                }
            }
        }

        public bool IsActive
        {
            get { return isActive; }
        }

        public void Start()
        {
            isActive = true;
            startTime = DateTime.Now;
            timer.Change(0, 15);
        }

        public void Stop()
        {
            timer.Change(0, Timeout.Infinite);
            isActive = false;
        }

        public void Dispose()
        {
            timer.Dispose();
        }

        protected virtual WaveFormStrategy GetStrategy(WaveForm waveForm)
        {
            switch (Config.WaveForm)
            {
                case WaveForm.Sine:
                    return WaveFormStrategies.Sine;
                case WaveForm.Square:
                    return WaveFormStrategies.Square;
                case WaveForm.Triangle:
                    return WaveFormStrategies.Triangle;
                case WaveForm.Sawtooth:
                    return WaveFormStrategies.Sawtooth;
                default:
                    throw new NotSupportedException("The wave form is not supported by this driver.");
            }
        }

        protected virtual void OnSample(double amplitude, DateTime timeStamp)
        {
            eventAggregator.GetEvent<SampleEvent>().Publish(new SampleData(this, amplitude, timeStamp));
        }

        private void FireSignalSample(object state)
        {
            DateTime now = DateTime.Now;
            TimeSpan time = now.Subtract(startTime);
            
            double amplitude = GetStrategy(Config.WaveForm)(Config.Amplitude, Config.Frequency, time.TotalSeconds);
            OnSample(amplitude, now);
        }
    }
}
